﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;

namespace Cerulean
{
  //[DebuggerNonUserCode]
  public class SkyDrive
  {
    SkyDrive() { RootFolders = new List<WebDavObject>(); }
    static SkyDrive _instance = new SkyDrive();
    static public SkyDrive Instance { get { return _instance; } }
    static readonly string[] __folderTypeNames = new string[] { "Folder", "Library" };
    static readonly XNamespace __nsAtom = "http://www.w3.org/2005/Atom";
    static readonly XNamespace __nsLive = "http://api.live.com/schemas";
    static readonly XNamespace __nsCloud = "http://schemas.microsoft.com/clouddocuments";
    string _passportToken, _fileDownloadToken, _cid, _username, _password, _WindowsLiveToken;
    Dictionary<string, string> ParsePassportParameters(string passportParameters)
    {
      var dic = new Dictionary<string, string>();
      if (!string.IsNullOrEmpty(passportParameters))
      {
        var parameters = passportParameters.Split(',');
        foreach (var parameter in parameters)
        {
          var pairs = parameter.Split('=');
          var key = pairs.Length > 0 ?
              System.Net.HttpUtility.UrlDecode(pairs[0]) : String.Empty;
          var value = pairs.Length > 1 ?
              System.Net.HttpUtility.UrlDecode(string.Join("=", pairs, 1, pairs.Length - 1)) : String.Empty;
          if (key.Length > 0)
          {
            if (key.Equals("DALogin") &&
                !value.StartsWith("http", StringComparison.OrdinalIgnoreCase))
            {
              value = String.Concat("https://", value);
            }
            dic[key] = value;
          }
        }
      }
      return dic;
    }
    readonly Regex _CidRegex = new Regex("(?i:cid.(?<cid>[0-9a-f]{16}))");
    readonly Regex _PassportAuthenticationRegex = new Regex("(?i:\\s*Passport(?<Version>\\d+(\\.\\d+)?)\\s*(?<Parameters>.*))");
    readonly Regex _AuthorizationTokenRegex = new Regex("t=(?<Token>.+)");
    public List<WebDavObject> RootFolders { get; private set; }
    public enum SharingLevel { Public, PublicUnlisted, Shared, Private, }
    Uri GetRootUri()
    {
      var decCid = Int64.Parse(_cid, System.Globalization.NumberStyles.HexNumber);
      return new Uri(
        string.Format(System.Globalization.CultureInfo.InvariantCulture,
        "http://cid-{0}.users.api.live.net/Users({1})/Files", _cid, decCid));
    }
    Uri GetFolderUri(string folderId)
    {
      return new Uri(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/Folders('{1}')", GetRootUri(), folderId));
    }
    Uri GetFileUri(string fileId)
    {
      return new Uri(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}/Files('{1}')", GetRootUri(), fileId));
    }
    Uri uriSkyDocsService = new Uri("http://docs.live.net/SkyDocsService.svc");
    Uri uriNexus = new Uri("https://nexus.passport.com/rdr/pprdr.asp");
    string soapGetWebAccountInfoRequest = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><GetWebAccountInfoRequest xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.microsoft.com/clouddocuments\"><BaseRequest><ClientAppId>SkyDrive Service Client</ClientAppId><Market>en-US</Market><SkyDocsServiceVersion>v1.0</SkyDocsServiceVersion></BaseRequest><GetReadWriteLibrariesOnly>false</GetReadWriteLibrariesOnly></GetWebAccountInfoRequest></s:Body></s:Envelope>";
    string _xmlRootFolders;
    void GetCid()
    {
      bool running = true;
      ThreadPool.QueueUserWorkItem((state) =>
      {
        WebClient wc = new WebClient() { AllowAutoRedirect = false };
        wc.Headers["SOAPAction"] = "GetWebAccountInfo";
        wc.Headers["Accept-Language"] = "en-US";
        wc.Headers["Accept"] = "text/xml";
        wc.Headers["Content-Type"] = "text/xml; charset=utf-8";
        wc.Headers[System.Net.HttpRequestHeader.Authorization] = _passportToken;
        wc.UploadStringCompleted += (s4, e4) =>
        {
          _cid = _CidRegex.Match(_xmlRootFolders = e4.Result).Groups[1].Value;
          running = false;
        };
        wc.UploadStringAsync(uriSkyDocsService, soapGetWebAccountInfoRequest);
      });
      while (running) Thread.Sleep(100);
    }
    string GetPassportToken(Uri uri)
    {
      string token = "NOT FOUND";
      bool running = true;
      ThreadPool.QueueUserWorkItem((state) =>
      {
        //provoke Passport challenge
        WebClient wc = new WebClient() { AllowAutoRedirect = false };
        wc.Encoding = Encoding.UTF8;
        wc.Headers["SOAPAction"] = "GetWebAccountInfo";
        wc.Headers["Accept-Language"] = "en-US";
        wc.Headers["Accept"] = "text/xml";
        wc.Headers["Content-Type"] = "text/xml; charset=utf-8";
        UploadStringCompletedEventHandler d1 = null;
        d1 = (s1, e1) =>
        {
          wc.UploadStringCompleted -= d1;
          string challenge = wc.ResponseHeaders["Www-Authenticate"];
          if (_PassportAuthenticationRegex.IsMatch(challenge))
          {
            //respond to challenge with username and password
            var matchPassportAuthentication = _PassportAuthenticationRegex.Match(challenge);
            var passportParameter = ParsePassportParameters(wc.ResponseHeaders["PassportURLs"]);
            string authPP = string.Format(
              "Passport{0} sign-in={1},pwd={2},OrgVerb={3},OrgUrl={4},{5}",
              matchPassportAuthentication.Groups["Version"].Value,
              HttpUtility.UrlEncode(_username),
              HttpUtility.UrlEncode(_password),
              "POST",
              uriSkyDocsService.AbsoluteUri,
              matchPassportAuthentication.Groups["Parameters"].Value);
            wc.Headers[HttpRequestHeader.Authorization] = authPP;
            //get DALogin
            DownloadStringCompletedEventHandler d2 = null;
            d2 = (s2, e2) =>
            {
              wc.DownloadStringCompleted -= d2;
              passportParameter = ParsePassportParameters(wc.ResponseHeaders["PassportURLs"]);
              string DALogin = passportParameter["DALogin"];
              wc.Headers["Authorization"] = authPP;
              DownloadStringCompletedEventHandler d3 = null;
              d3 = (s3, e3) =>
              {
                wc.DownloadStringCompleted -= d3;
                if (e3.Error != null)
                  throw new UnauthorizedAccessException("Invalid username or password", e3.Error);
                var authenticationInfo = wc.ResponseHeaders["Authentication-Info"];
                matchPassportAuthentication = _PassportAuthenticationRegex.Match(authenticationInfo);
                passportParameter = ParsePassportParameters(matchPassportAuthentication.Groups["Parameters"].Value);
                token = string.Format("Passport{0} from-PP={1}", matchPassportAuthentication.Groups["Version"].Value, passportParameter["from-PP"]);
                running = false;
              };
              wc.DownloadStringCompleted += d3;
              wc.DownloadStringAsync(new Uri(DALogin));
            };
            wc.DownloadStringCompleted += d2;
            wc.DownloadStringAsync(uriNexus);
          }
        };
        wc.UploadStringCompleted += d1;
        wc.UploadStringAsync(uri, soapGetWebAccountInfoRequest);
      });
      while (running) Thread.Sleep(200);
      return token;
    }
    public void CreateFolder(string folderpath)
    {
      folderpath = folderpath.Replace('\\', '/');
      if (WebDavObject.ByPath(folderpath) == null)
      {
        int lastSlashPosition = folderpath.LastIndexOf('/');
        if (lastSlashPosition == 0)
        {
          CreateRootFolder(folderpath, "Document", SharingLevel.Private, true);
        }
        else
        {
          string foldername = folderpath.Substring(lastSlashPosition + 1);
          string parentpath = folderpath.Substring(0, lastSlashPosition);
          if (parentpath.Length > 1 && WebDavObject.ByPath(parentpath) == null)
            CreateFolder(parentpath);
          CreateSubfolder(parentpath, foldername);
        }
      }
    }
    /// <summary>
    /// Creates a folder in a pre-existing parent folder.
    /// </summary>
    /// <param name="parentpath">Full path to a folder that exists</param>
    /// <param name="foldername">Name for a folder that is to be created as a child of the specified parent</param>
    void CreateSubfolder(string parentpath, string foldername)
    {
      FileSystemAddRef();
      Uri parentUri = GetFolderUri(WebDavObject.ByPath(parentpath).ResourceId);
      string createSubfolderTemplate = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<entry xmlns:live=\"http://api.live.com/schemas\" xmlns=\"http://www.w3.org/2005/Atom\">\r\n  <title>{0}</title>\r\n  <live:type>Folder</live:type>\r\n  <live:resolveNameConflict>true</live:resolveNameConflict>\r\n</entry>";
      string request = string.Format(createSubfolderTemplate, foldername);
      WebClient wc = new WebClient() { AllowAutoRedirect = false };
      wc.Encoding = Encoding.UTF8;
      wc.Headers["AppId"] = "1140851978";
      wc.Headers["Accept-Language"] = "en-US";
      wc.Headers["Accept"] = wc.Headers["Content-Type"] = "application/atom+xml";
      wc.Headers[HttpRequestHeader.Authorization] = _WindowsLiveToken;
      UploadStringCompletedEventHandler d1 = null;
      bool running = true;
      d1 = (s, e) =>
      {
        wc.UploadStringCompleted -= d1;
        if (e.Error == null)
        {
          XDocument xdoc = XDocument.Parse(e.Result);
          string title = xdoc.Descendants(__nsAtom + "title").First().Value;
          string resourceId = xdoc.Descendants(__nsLive + "resourceId").First().Value;
          WebDavObject newFolder = new WebDavObject(title, resourceId, WebDavObject.ByPath(parentpath), "Folder");
          RootFolders.Add(newFolder);
        }
        running = false;
        FileSystemRelease(e.Error == null);
      };
      wc.UploadStringCompleted += d1;
      wc.UploadStringAsync(parentUri, "POST", request);
      while (running) Thread.Sleep(100);
    }
    public void CreateRootFolder(string name, string category, SharingLevel sharingLevel, bool blocking = false)
    {
      if (name.StartsWith("/")) name = name.Remove(0, 1);
      if (WebDavObject.ByPath(string.Format("/{0}", name)) == null)
      {
        FileSystemAddRef();
        Uri rootUri = GetRootUri();
        string createRootFolderTemplate = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<entry xmlns:live=\"http://api.live.com/schemas\" xmlns=\"http://www.w3.org/2005/Atom\">\r\n  <title>{0}</title>\r\n  <live:type>Library</live:type>\r\n  <live:category>{1}</live:category>\r\n  <live:sharingLevel>{2}</live:sharingLevel>\r\n</entry>";
        string request = string.Format(createRootFolderTemplate, name, category, sharingLevel);
        WebClient wc = new WebClient();
        wc.Encoding = Encoding.UTF8;
        wc.Headers["AppId"] = "1140851978";
        wc.Headers["Accept-Language"] = "en-US";
        wc.Headers["Accept"] = wc.Headers["Content-Type"] = "application/atom+xml";
        wc.Headers[HttpRequestHeader.Authorization] = _WindowsLiveToken;
        UploadStringCompletedEventHandler d1 = null;
        bool running = true;
        d1 = (s, e) =>
        {
          wc.UploadStringCompleted -= d1;
          if (e.Error == null)
          {
            XDocument xdoc = XDocument.Parse(e.Result);
            string title = xdoc.Descendants(__nsAtom + "title").First().Value;
            string resourceId = xdoc.Descendants(__nsLive + "resourceId").First().Value;
            WebDavObject newFolder = new WebDavObject(title, resourceId, null, "Folder");
            RootFolders.Add(newFolder);
          }
          else
            throw new IOException(string.Format("Unable to create '{0}'", name), e.Error);
          running = false;
          FileSystemRelease(e.Error == null);
        };
        wc.UploadStringCompleted += d1;
        wc.UploadStringAsync(rootUri, "POST", request);
        while (blocking && running) Thread.Sleep(100);
      }
    }
    public event EventHandler FileSystemChanged;
    int _FileSystemBusyCount = 0;
    public void FileSystemAddRef()
    {
      lock (this)
      {
        _FileSystemBusyCount++;
        IsFileSystemReady = false;
        Debug.WriteLine(_FileSystemBusyCount);
      }
    }
    public void FileSystemRelease(bool notify = true)
    {
      lock (this) _FileSystemBusyCount--;
      Debug.WriteLine(_FileSystemBusyCount);
      if (notify && _FileSystemBusyCount == 0 && FileSystemChanged != null)
      {
        IsFileSystemReady = true;
        FileSystemChanged(null, EventArgs.Empty);
      }
    }
    public bool IsFileSystemReady { get; private set; }
    internal void GetFolderItems(WebDavObject folder, string itemType = null)
    {
      FileSystemAddRef();
      ThreadPool.QueueUserWorkItem((state) =>
      {
        if (_WindowsLiveToken == null) return;
        Uri folderUri = GetFolderUri(folder.ResourceId);
        WebClient wc = new WebClient() { AllowAutoRedirect = false };
        wc.Headers["AppId"] = "1140851978";
        wc.Headers[HttpRequestHeader.AcceptLanguage] = "en-US";
        wc.Headers[HttpRequestHeader.Accept] = "application/atom+xml";
        wc.Headers[HttpRequestHeader.Authorization] = _WindowsLiveToken;
        wc.DownloadStringCompleted += (s, e) =>
        {
          try
          {
            if (e.Error == null)
            {
              folder.Children.Clear();
              folder.Children.AddRange(
                from p in XDocument.Parse(e.Result).Descendants(__nsAtom + "entry")
                where (itemType == null) || (p.Element(__nsAtom + "type").Value == itemType)
                select new WebDavObject(
                  p.Element(__nsAtom + "title").Value,
                  p.Element(__nsLive + "resourceId").Value,
                  folder,
                  p.Element(__nsLive + "type").Value,
                  __folderTypeNames.Contains(p.Element(__nsLive + "type").Value) ? null :
                    new Uri(p.Element(__nsAtom + "content").Attribute("src").Value)));
            }
            else
              throw new IOException("Unable to load file system", e.Error);
          }
          finally
          {
            FileSystemRelease();
          }
        };
        wc.DownloadStringAsync(folderUri);
      });
    }
    public string LoadString(string filepath)
    {
      bool running = true;
      string result = null;
      DownloadStream(filepath, (s, e) =>
      {
        StreamReader sr = new StreamReader(e.Stream);
        result = sr.ReadToEnd();
        running = false;
      });
      while (running) Thread.Sleep(100);
      return result;
    }
    void DownloadStream(string filepath, EventHandler<DownloadStreamReadyArgs> completed)
    {
      if (completed == null) throw new ArgumentNullException();
      Uri uriFile = GetWebDavUri(filepath);
      if (_fileDownloadToken == null)
        _fileDownloadToken = GetPassportToken(uriFile);
      WebClient wc = new WebClient() { AllowAutoRedirect = false };
      wc.Headers[HttpRequestHeader.Accept] = "*/*";
      wc.Headers[HttpRequestHeader.Authorization] = _fileDownloadToken;
      OpenReadCompletedEventHandler d1 = null;
      d1 = (s1, e1) =>
      {
        wc.OpenReadCompleted -= d1;
        if (e1.Error == null)
        {
          wc.Headers[HttpRequestHeader.Accept] = "*/*";
          wc.Headers[HttpRequestHeader.Authorization] = _passportToken;
          OpenReadCompletedEventHandler d2 = (s2, e2) =>
          {
            if (e2.Error == null)
              completed(this, new DownloadStreamReadyArgs() { Stream = e2.Result });
            else
              throw new IOException(string.Format("Unable to read file '{0}'", filepath), e2.Error);
          };
          wc.OpenReadCompleted += d2;
          wc.OpenReadAsync(new Uri(wc.ResponseHeaders["Location"]));
        }
        else
        {
          throw new IOException(string.Format("Unable to read file '{0}'", filepath), e1.Error);
        }
      };
      wc.OpenReadCompleted += d1;
      wc.OpenReadAsync(uriFile);
    }
    public void SaveString(string filepath, string value, bool blocking = false)
    {
      FileSystemAddRef();
      filepath = filepath.Replace('\\', '/');
      int lastSlashPosition = filepath.LastIndexOf('/');
      string folderpath = filepath.Substring(0, lastSlashPosition);
      string filename = filepath.Substring(lastSlashPosition + 1);
      CreateFolder(folderpath);
      WebDavObject folder = WebDavObject.ByPath(folderpath);
      string folderId = folder.ResourceId;
      string command = string.Format("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<entry xmlns:live=\"http://api.live.com/schemas\" xmlns=\"http://www.w3.org/2005/Atom\">\r\n  <title>{0}</title>\r\n  <live:type>Document</live:type>\r\n  <live:resolveNameConflict>true</live:resolveNameConflict>\r\n</entry>", filename);
      string mpBoundary = string.Format(System.Globalization.CultureInfo.InvariantCulture, "---------------------------{0:x}", DateTime.Now.Ticks);
      string mpLastBoundary = string.Format(System.Globalization.CultureInfo.InvariantCulture, "--{0}--", mpBoundary);
      var uriFolder = GetFolderUri(folderId);
      string data = string.Format("--{0}\r\nMIME-Version: 1.0\r\nContent-Type: application/atom+xml\r\n\r\n{1}\r\n--{0}\r\nMIME-Version: 1.0\r\nContent-Type: text/plain\r\n\r\n{2}\r\n{3}\r\n", mpBoundary, command, value, mpLastBoundary);
      WebClient wc = new WebClient() { AllowAutoRedirect = false };
      wc.Encoding = Encoding.UTF8;
      wc.Headers["AppId"] = "1140851978"; //skydrive is itself an app
      wc.Headers[HttpRequestHeader.AcceptLanguage] = "en-US";
      wc.Headers[HttpRequestHeader.ContentType] = string.Format("multipart/related; type=\"application/atom+xml\"; boundary=\"{0}\"", mpBoundary);
      wc.Headers[HttpRequestHeader.ContentLength] = data.Length.ToString();
      wc.Headers[HttpRequestHeader.Authorization] = _WindowsLiveToken;
      bool running = true;
      UploadStringCompletedEventHandler d1 = null;
      d1 = (s1, e1) =>
      {
        wc.UploadStringCompleted -= d1;
        if (e1.Error == null)
        {
          folder.Children.Add(new WebDavObject(filename, XDocument.Parse(e1.Result).Root.Element(__nsLive + "resourceId").Value, folder, "Document"));
          Debug.WriteLine("SaveString(\"{0}\",\"{1}\")", filepath, value);
        }
        else
          throw new IOException(string.Format("Unable to create file '{0}'", filepath), e1.Error);
        running = false;
        FileSystemRelease(e1.Error == null);
      };
      wc.UploadStringCompleted += d1;
      wc.UploadStringAsync(uriFolder, data);
      while (blocking && running) Thread.Sleep(100);
    }
    /// <summary>
    /// Delete the named resource. For folders, the delete is recursive and includes contained files.
    /// </summary>
    /// <param name="filepath"></param>
    public void Delete(string filepath)
    {
      WebDavObject webfile = WebDavObject.ByPath(filepath);
      if (webfile == null)
        throw new FileNotFoundException(string.Format("'{0}' was absent from the filesystem cache", filepath));
      WebClient wc = new WebClient() { AllowAutoRedirect = false };
      wc.Headers["AppId"] = "1140851978";
      wc.Headers[HttpRequestHeader.ContentType] = "application/atom+xml";
      wc.Headers[HttpRequestHeader.Authorization] = _WindowsLiveToken;
      Uri uri = __folderTypeNames.Contains(webfile.Type) ? GetFolderUri(webfile.ResourceId) : GetFileUri(webfile.ResourceId);
      wc.UploadStringCompleted += (s, e) =>
      {
        if (e.Error == null)
          WebDavObject.Remove(filepath);
        else
          throw new IOException(string.Format("Unable to delete '{0}'", filepath), e.Error);
      };
      wc.UploadStringAsync(uri, "DELETE", string.Empty);
    }

    static readonly DateTime Epoch1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);

    string GetWindowsLiveToken()
    {
      string token = null;
      bool running = true;
      ThreadPool.QueueUserWorkItem((state) =>
      {
        string challenge = String.Format("Passport1.4 ct={0:0},rver=6.1.6206.0,wp=MBI,lc=1033,id=250206", (DateTime.Now - Epoch1970).TotalSeconds);
        Uri uriWindowsLive = GetRootUri();
        WebClient wc = new WebClient() { AllowAutoRedirect = false };
        DownloadStringCompletedEventHandler d1 = null;
        var matchPassportAuthentication = _PassportAuthenticationRegex.Match(challenge);
        d1 = (s1, e1) =>
        {
          wc.DownloadStringCompleted -= d1;
          var passportParameter = ParsePassportParameters(wc.ResponseHeaders["PassportURLs"]);
          var challengeResponse = String.Format("Passport{0} sign-in={1},pwd={2},OrgVerb={3},OrgUrl={4},{5}",
            matchPassportAuthentication.Groups["Version"].Value,
            HttpUtility.UrlEncode(_username),
            HttpUtility.UrlEncode(_password),
            "GET",
            uriNexus,
            matchPassportAuthentication.Groups["Parameters"].Value);
          wc.Headers[HttpRequestHeader.Authorization] = challengeResponse;
          DownloadStringCompletedEventHandler d2 = null;
          d2 = (s2, e2) =>
          {
            wc.DownloadStringCompleted -= d2;
            var authenticationInfo = wc.ResponseHeaders["Authentication-Info"];
            matchPassportAuthentication = _PassportAuthenticationRegex.Match(wc.ResponseHeaders["Authentication-Info"]);
            passportParameter = ParsePassportParameters(matchPassportAuthentication.Groups["Parameters"].Value);
            token = String.Format("WLID1.0 t={0}", _AuthorizationTokenRegex.Match(passportParameter["from-PP"]).Groups["Token"].Value);
            running = false;
          };
          wc.DownloadStringCompleted += d2;
          wc.DownloadStringAsync(new Uri(passportParameter["DALogin"]));
        };
        wc.DownloadStringCompleted += d1;
        wc.DownloadStringAsync(uriNexus);
      });
      while (running) Thread.Sleep(100);
      return token;
    }
    void LoadFileSystem()
    {
      FileSystemAddRef();
      XDocument xdoc = XDocument.Parse(_xmlRootFolders);
      _xmlRootFolders = null;
      RootFolders.Clear();
      RootFolders.AddRange(
        from p in xdoc.Descendants(__nsCloud + "Library")
        select new WebDavObject(
          p.Element(__nsCloud + "DisplayName").Value,
          p.Element(__nsCloud + "ResourceId").Value,
          null,
          "Library")
        );
      FileSystemRelease();
    }
    static readonly List<string> __WebDavHosts = new List<string>()
    {
      "bqvkvy.docs.live.net",
      "9iwlkc.docs.live.net",
      "cmlubp.docs.live.net",
      "mo4vwm.docs.live.net",
      "cbdg9k.docs.live.net",
      "phjcvr.docs.live.net",
      "bceulv.docs.live.net",
      "omd4hq.docs.live.net",
      "lmdvz4.docs.live.net",
      "iaep8q.docs.live.net",
      "twpfax.docs.live.net",
      "bpapni.docs.live.net"
    };
    string RandomWebDavHost()
    {
      var rnd = new Random(__WebDavHosts.Count);
      var webDavHost = __WebDavHosts[rnd.Next(__WebDavHosts.Count)];
      return webDavHost;
    }
    Uri GetWebDavUri(string filepath)
    {
      return new Uri(string.Format(System.Globalization.CultureInfo.InvariantCulture, "https://{0}/{1}{2}", RandomWebDavHost(), _cid, filepath));
    }
    public void Authenticate(string username, string password)
    {
      ThreadPool.QueueUserWorkItem((state) =>
      {
        _username = username;
        _password = password;
        _passportToken = GetPassportToken(uriSkyDocsService);
        GetCid();
        _WindowsLiveToken = GetWindowsLiveToken();
        LoadFileSystem();
      });
    }
  }
}
